#ifndef VOLUME_HPP
#define VOLUME_HPP

#include <QtCore>
#include <Eigen/Core>
#include <cstring>

#include "octnode.hpp"
#include "volumereader.hpp"
#include "slice.hpp"

using namespace Eigen;

class Slice;

class Volume {
  public:
    enum VoxelLabel { Background=0,Internal=128 };
    Volume();
    Volume(const Vector3i &dim,unsigned char initial=0);
    Volume(const QString &fileName,bool smallFootprint=true,bool markInternal=true);
    Volume(const Vector3i &dim,unsigned char *data);
    Volume(const Volume &other);
    Volume(const Slice &slice);
    Volume(const Volume &other,unsigned char *data);
    Volume(const Volume &first,const Volume &second);
    ~Volume();
    const Vector3i & size() const;
    const Vector3d & position() const;
    void setPosition(const Vector3d &pos);
    int size(int dim);
    int maxSize();
    int minSize();
    int numVoxels();
    int getNumSurfaceVoxels();
    int getNumEnabledVoxels();
    double getVoxelSize() const;
    OctNode * getOctreeRoot();
    void setVoxelSize(double delta);
    void readVolumeFile(const QString &fileName); //TODO deprecate
    void read(const QString &fileName);
    void writeVolumeFile(); //TODO deprecate
    void write();
    void write(const QString &fileName);
    void write(const QDir &dir);
    void writeVolumeFile(const QString &fileName); //TODO deprecate
    void setFileName(const QString &fileName);
    const Vector3i & getHeadVoxel() const;
    void setSkeleton(bool skeleton);
    bool isSkeleton() const;
    unsigned char getVoxel(int index);
    unsigned char getVoxel(int x,int y,int z);
    unsigned char getVoxel(const Vector3i &indices);
    void setVoxel(int index,unsigned char value);
    void setVoxel(int x,int y,int z,unsigned char value);
    void setVoxel(const Vector3i &indices,unsigned char value);
    unsigned char * data();
    const unsigned char * data() const;
    unsigned char * data(int x,int y, int z);
    const unsigned char * data(int x,int y,int z) const;
    bool isValidIndex(const Vector3i &index);
    void normalise();
    void createSmallFootprint(bool markInternal);
    const QHash<long,unsigned char> * getSparseData() const;
    void findHeadVoxel();
    bool binaryEqual(const Volume &other) const;
    Slice slice();
    Slice slice(const Vector3i &start,const Vector3i &end);

    //class methods
    static QString getValidFormats();
    static QStringList getValidFormatList();
  private:
    //fields
    int num;
    int surfaceVoxelCount;
    int enabledVoxelCount;
    bool skeleton;
    Vector3i dim;
    double delta;
    Vector3d pos;
    Vector3i head;
    unsigned char *voxels;
    QString fileName;
    OctNode *octRoot;
    QHash<long,unsigned char> *sparseData;

    //methods
    void markInternalVoxels();
    bool isSurfaceVoxel(int x,int y,int z);
    void buildOctree();
    void addSubOctree(OctNode *subTreeRoot);
    void recreateVoxels();
    void readRawFile(const QString &fileName);
    void readRawXFile(const QString &fileName,bool compressed=false);
    void readVoxFile(const QString &fileName);
    void readVoxmlFile(const QString &fileName);
    void readPgmFile(const QString &fileName);
    void writeRawFile(const QString &fileName);
    void writeRawXFile(const QString &fileName,bool compressed=false);
    void writeVoxFile(const QString &fileName);
    void writeVoxmlFile(const QString &fileName);
    void writePgmFile(const QString &fileName);
};

#endif
